#!/usr/bin/perl
use FindBin;
use lib $FindBin::Bin;
use lib "$FindBin::Bin/lib";
use lib "$FindBin::Bin/../lib";
use lib "$FindBin::Bin/../plib/lib/perl5";

use strict;

use JSON;
use FireWallBase;
use File::Basename;
use Getopt::Long;
use AutoExecUtils;
use CollectObjCat;

sub usage {
    my $pname = $FindBin::Script;
    print("INFO: :option --node or --type not defined.\n");
    print("$pname --node <node> --type <type> .\n");
    exit(0);
}

#README
#如果新增其他品牌交换机，OID设置有问题，可能需要修改FireWallBase.pm, 修改开头的$BRANDS的定义，增加品牌名。
#参考：FireWallHuawei.pm
#从FireWallBase.pm继承重载before方法调整snmp oid的设置
#重载after方法，进行数据的调整或者通过其他途径补充数据
#测试：firewallcollector --node '{"host":"192.168.0.252","password":"public"}'

sub main {
    $| = 1;    #不对输出进行buffer，便于实时看到输出日志
    AutoExecUtils::setEnv();

    my ( $node, $type );

    my $sshAccount;
    my $defaultCommunity;
    my $timeout   = 10;
    my $isVerbose = 0;

    GetOptions(
        'verbose=i'    => \$isVerbose,
        'node=s'       => \$node,
        'type=s'       => \$type,
        'sshaccount=s' => \$sshAccount,
        'community=s'  => \$defaultCommunity,
        'timeout=i'    => \$timeout
    );

    my $nodeInfo  = {};
    my $hasOptErr = 0;
    if ( not defined($node) ) {
        $node = $ENV{AUTOEXEC_NODE};
    }

    if ( not defined($node) or $node eq '' ) {
        $hasOptErr = 1;
    }
    else {
        $nodeInfo = from_json($node);
    }

    if ( $hasOptErr == 1 ) {
        usage();
    }

    if ( not defined($timeout) ) {
        $timeout = 10;
    }

    my $hostname     = $nodeInfo->{host};
    my $community    = $nodeInfo->{password};
    my $protocol     = $nodeInfo->{protocol};
    my $protocolPort = $nodeInfo->{protocolPort};

    #version (i.e. 'snmpv1', 'snmpv2c', or 'snmpv3')
    if ( not defined($protocol) or $protocol eq '' or $protocol eq 'snmp' ) {
        $protocol = 'snmpv2c';
    }

    if ( not defined($community) or $community eq '' ) {

        #如果执行节点给出的协议不是snmp或者没有给出密码，则使用默认密码选项给出的团体字
        $community = $defaultCommunity;
    }
    if ( not defined($community) or $community eq '' ) {
        $community = 'public';
    }

    my $usmData  = {};
    my $authData = $nodeInfo->{authData};
    if ( defined($authData) ) {

        #snmpv3
        $usmData->{version}      = 'snmpv3';
        $usmData->{username}     = $authData->{username};
        $usmData->{authpassword} = $authData->{authPassword};
        $usmData->{authprotocol} = $authData->{authProtocol};
        $usmData->{privpassword} = $authData->{privPassword};
        $usmData->{privprotocol} = $authData->{privProtocol};

        if ( not defined( $usmData->{username} ) ) {
            $usmData->{username} = $nodeInfo->{username};
        }
        if ( not defined( $usmData->{authpassword} ) ) {
            $usmData->{authpassword} = $community;
        }
    }
    else {
        #snmpv2
        $usmData->{version}   = $protocol;
        $usmData->{community} = $community;
    }

    my $sshAccountInfo = {};
    if ( defined($sshAccount) and $sshAccount ne '' ) {
        if ( $sshAccount =~ /^\s*(\d+)\s*:\s*(\w+)\/(.*?)\s*$/ ) {
            my $protocolPort = int($1);
            if ( $protocolPort == 0 ) {
                $protocolPort = 22;
            }
            $sshAccountInfo->{protocolPort} = $protocolPort;
            $sshAccountInfo->{username}     = $2;
            $sshAccountInfo->{password}     = $3;
        }
        elsif ( $sshAccount =~ /^\s*(\w+)\/(.*?)\s*$/ ) {
            $sshAccountInfo->{protocolPort} = 22;
            $sshAccountInfo->{username}     = $1;
            $sshAccountInfo->{password}     = $2;
        }
    }

    my $isFailed = 0;

    my $brand        = $type;
    my $firewallBase = FireWallBase->new( hostname => $hostname, port => $protocolPort, timeout => $timeout, node => $nodeInfo, sshAccount => $sshAccountInfo, %$usmData );
    if ( not defined($type) or $type eq '' or $type eq 'auto' ) {
        $brand = $firewallBase->getBrand();
    }

    if ( not defined($brand) or $brand eq '' ) {
        $isFailed = 1;
        print("ERROR: Can not discover the firewall device brand.\n");
    }
    else {
        print("INFO: FireWall brand: $brand.\n");
    }

    my $firewallIns;
    my $firewallClass = "FireWall$brand";
    if ( -e "$FindBin::Bin/$firewallClass.pm" or -e "$FindBin::Bin/lib/$firewallClass.pm" ) {
        print("INFO: Has defined class $firewallClass, try to load it.\n");
        eval {
            require "$firewallClass.pm";
            $firewallIns = $firewallClass->new( hostname => $hostname, community => $community, timeout => $timeout, node => $nodeInfo, sshAccount => $sshAccountInfo );
        };
        if ($@) {
            $isFailed = 1;
            print("WARN: Load $firewallClass failed, $@");
            print("WARN: Fall back to FireWallBase class.\n");
            $firewallIns = $firewallBase;
        }
        else {
            print("INFO: Class $firewallClass loaded.\n");
        }
    }
    else {
        print("INFO: $firewallClass not found.\n");
        print("INFO: Fall back to FireWallBase class.\n");
        $firewallIns = $firewallBase;
    }

    my $data = $firewallIns->collect();

    $data->{BRAND} = $brand;
    if ( not defined( $data->{VENDOR} ) ) {
        $data->{VENDOR} = $data->{BRAND};
    }

    my $objCat = CollectObjCat->get('SECDEV');
    $data->{_OBJ_CATEGORY} = $objCat;
    $data->{_OBJ_TYPE}     = 'FireWall';
    $data->{_APP_TYPE}     = $brand;
    $data->{MGMT_IP}       = $nodeInfo->{host};
    $data->{PK}            = CollectObjCat->getPK($objCat);
    $data->{RESOURCE_ID}   = $nodeInfo->{resourceId};

    my $out = {};
    $out->{DATA} = [$data];
    AutoExecUtils::saveOutput($out);
    if ( $isVerbose == 1 ) {
        print("==================\n");
        print( to_json( $data, { pretty => 1 } ) );
        print("==================\n");
    }

    return $isFailed;
}

exit main();
